/************************************************************************
This file is part of java core libraries for the simpleRTJ virtual machine.

This file is covered by the GNU GPL with the following exception:
  As a special exception, the copyright holders of this library give you permission
  to link this library with independent modules to produce an executable, regardless
  of the license terms of these independent modules, and to copy and distribute the
  resulting executable under terms of your choice, provided that you also meet, for
  each linked independent module, the terms and conditions of the license of that
  module. An independent module is a module which is not derived from or based on
  this library. If you modify this library, you may extend this exception to your
  version of the library, but you are not obligated to do so. If you do not wish
  to do so, delete this exception statement from your version.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL RTJ COMPUTING BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

Copyright (c) 2000-2002 RTJ Computing Pty. Ltd. All rights reserved.
***********************************************************************/
package javax.memory;

/**
 * This is a base class for all types of memory classes. It provides basic access methods
 * to the physical memory of the target device. <br>
 * Memory address postincrement or postdecrement is disabled by default.
 * <p>
 *
 * In order to use this class in your application follow the following steps:<br>
 *    1. Locate the MemoryRegion.c in the source/javax/memory/native directory<br>
 *    2. Copy this file into your project's native code directory and
 *       make it part of the native code building process.
 */
public class MemoryRegion
{
    /** Contains the start address of a memory region. */
    protected int regStart = 0;
    /** Contains the end address of a memory region. The end address is exclusive. */
    protected int regEnd = 0;
    /** Memory offset withing the memory region. */
    protected int memOffs = 0;

    /** Post increment flag for read/write operations. */
    protected boolean postIncrement = false;
    /** Post decrement flag for read/write operations. */
    protected boolean postDecrement = false;

    /**
     * Creates a new memory region object.
     *
     * @param start starting address of a new memory region.
     * @param length length of a new memory region.
     * @exception InvalidMemoryRegionException is thrown when the requested region
     *      is invalid, i.e. overlaps with some other memory regions or there is no
     *      physical memory at specified location.
     */
    public MemoryRegion(int start, int length) throws InvalidMemoryRegionException
    {
        if (!regionOk(start, length))
            throw new InvalidMemoryRegionException();

        regStart = start;
        regEnd = start + length;
        memOffs = 0;
    }

    /**
     * Sets or clears the post increment flag. If set, all consequent memory
     * operations will increment memory pointer.
     *
     * @param Enable    new state for post increment flag. If true is specified then
     *          post decrement flag is cleared.
     */
    public void setPostIncrement(boolean Enable)
    {
        postIncrement = Enable;
        if (Enable)
            postDecrement = false;
    }

    /**
     * Sets or clears the post decrement flag. If set, all consequent memory
     * operations will decrement memory base address.
     *
     * @param Enale     new state for post decrement flag. If true is specified then
     *          post increment flag is cleared.
     */
    public void setPostDecrement(boolean Enable)
    {
        postDecrement = Enable;
        if (Enable)
            postIncrement = false;
    }

    /**
     * Sets the memory offset to a new value. The new offset will be used for the
     * consequent memory read/write operations.
     *
     * @param newOffset     new value for memory offset.
     * @exception RegionAddressOutOfBoundsException is thrown when a new offset is out of bounds
     *      of this memory region.
     */
    public void setOffset(int newOffset) throws RegionAddressOutOfBoundsException
    {
        if (newOffset < 0 || regStart + newOffset >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        memOffs = newOffset;
    }

    /**
     * Writes a <code>byte</code> into memory at current offset. Method setOffset()
     * should be called prior calling this method to setup the offset pointer.<br>
     * Offset pointer will be modified by 1 if post-increment is enabled or -1 if
     * post-decrement is enabled.
     *
     * @param value  a byte value that will be written into memory. Only lower 8 bits are written.
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     */
    public void writeByte(int value) throws RegionAddressOutOfBoundsException
    {
        if (regStart + memOffs < regStart || regStart + memOffs >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        writeByte0(regStart + memOffs, value);
        if (postIncrement)
            memOffs++;
        else if (postDecrement)
            memOffs--;
    }

    /**
     * Writes a <code>short</code> into memory at current offset. Method setOffset()
     * should be called prior calling this method to setup the offset pointer.<br>
     * Offset pointer will be modified by 2 if post-increment is enabled or -2 if
     * post-decrement is enabled.
     *
     * @param value  a short value that will be written into memory. Only lower 16 bits are written.
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     */
    public void writeShort(int value) throws RegionAddressOutOfBoundsException
    {
        if (regStart + memOffs < regStart || regStart + memOffs >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        writeShort0(regStart + memOffs, value);
        if (postIncrement)
            memOffs += 2;
        if (postDecrement)
            memOffs -= 2;
    }

    /**
     * Writes a <code>int</code> into memory at current offset. Method setOffset()
     * should be called prior calling this method to setup the offset pointer.<br>
     * Offset pointer will be modified by 4 if post-increment is enabled or -4 if
     * post-decrement is enabled.
     *
     * @param value  a integer value (32 bit) that will be written into memory.
     * @exception RegionAddressOutOfBounds is thrown when trying to access data out of bounds
     *      of this memory region.
     */
    public void writeInt(int value) throws RegionAddressOutOfBoundsException
    {
        if (regStart + memOffs < regStart || regStart + memOffs >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        writeInt0(regStart + memOffs, value);
        if (postIncrement)
            memOffs += 4;
        if (postDecrement)
            memOffs -= 4;
    }

    /**
     * Reads a <code>byte</code> from the memory region at current offset. Method setOffset()
     * should be called prior calling this method to setup the offset pointer.<br>
     * Offset pointer will be modified by 1 if post-increment is enabled or -1 if
     * post-decrement is enabled.
     *
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     */
    public int readByte() throws RegionAddressOutOfBoundsException
    {
        if (regStart + memOffs < regStart || regStart + memOffs >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        int retval = readByte0(regStart + memOffs);

        if (postIncrement)
            memOffs++;
        if (postDecrement)
            memOffs--;

        return retval;
    }

    /**
     * Reads a <code>short</code> from the memory region at current offset. Method setOffset()
     * should be called prior calling this method to setup the offset pointer.<br>
     * Offset pointer will be modified by 2 if post-increment is enabled or -2 if
     * post-decrement is enabled.
     *
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     */
    public int readShort() throws RegionAddressOutOfBoundsException
    {
        if (regStart + memOffs < regStart || regStart + memOffs >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        int retval = readShort0(regStart + memOffs);

        if (postIncrement)
            memOffs += 2;
        if (postDecrement)
            memOffs -= 2;

        return retval;
    }

    /**
     * Reads a <code>int</code> from the memory region at current offset. Method setOffset()
     * should be called prior calling this method to setup the offset pointer.<br>
     * Offset pointer will be modified by 4 if post-increment is enabled or -4 if
     * post-decrement is enabled.
     *
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     */
    public int readInt() throws RegionAddressOutOfBoundsException
    {
        if (regStart + memOffs < regStart || regStart + memOffs >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        int retval = readInt0(regStart + memOffs);

        if (postIncrement)
            memOffs += 4;
        if (postDecrement)
            memOffs -= 4;

        return retval;
    }

    /**
     * Writes <code>byte</code> array to the memory starting at the current offset.
     * Method setOffset() should be called prior calling this method to setup the offset pointer.<br>
     * The offset pointer will be modified by the number of bytes written if post-increment
     * or post-decrement flag is set.
     *
     * @param array a source byte array
     * @param start starting offset in the array
     * @param length number of bytes to write
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     * @exception IndexOutOfBoundsException is thrown when trying to access array data that out of bounds
     *      of the input byte array.
     */
    public void writeBytes(byte[] array, int start, int length)
        throws RegionAddressOutOfBoundsException, IndexOutOfBoundsException
    {
        if (start + length > array.length)
            throw new IndexOutOfBoundsException();
        if (regStart + memOffs < regStart || regStart + memOffs + length >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        writeBytes0(regStart + memOffs, array, start, length);
        if (postIncrement)
            memOffs += length;
        if (postDecrement)
            memOffs -= length;
    }

    /**
     * Reads <code>byte</code> values from the memory starting at the current offset to the
     * destination byte array.
     * Method setOffset() should be called prior calling this method to setup the offset pointer.<br>
     * The offset pointer will be modified by number of bytes read if post-increment
     * or post-decrement flag is set.
     *
     * @param array destination byte array
     * @param start starting offset in the array
     * @param length number of bytes to read
     * @exception RegionAddressOutOfBoundsException is thrown when trying to access data out of bounds
     *      of this memory region.
     * @exception IndexOutOfBoundsException is thrown when trying to access array data that out of bounds
     *      of the destination byte array.
     */
    public void readBytes(byte[] array, int start, int length)
        throws RegionAddressOutOfBoundsException, IndexOutOfBoundsException
    {
        if (start + length > array.length)
            throw new IndexOutOfBoundsException();
        if (regStart + memOffs < regStart || regStart + memOffs + length >= regEnd)
            throw new RegionAddressOutOfBoundsException();

        readBytes0(regStart + memOffs, array, start, length);
        if (postIncrement)
            memOffs += length;
        if (postDecrement)
            memOffs -= length;
    }


    /**
     * Verifies if the requested meory area is valid. Derived classes should override
     * this method if memory type specific checks should be performed. <br>
     * Default check performed by this method is to verify that the requested memory
     * region doesn't overlap with the garbage collected heap.
     *
     * @param start starting address of a new memory region.
     * @param length length of a new memory region.
     * @return true if memory region validity test passes, otherwise false is returned.
     */
    protected native boolean regionOk(int start, int length);

    /*
     * Native methods that write 8, 16 and 32 bit values into memory.
     */
    protected static native boolean writeByte0(int address, int value);
    protected static native boolean writeShort0(int address, int value);
    protected static native boolean writeInt0(int address, int value);
    protected static native boolean writeBytes0(int address, byte[] src, int start, int length);

    /*
     * Native methods that read 8, 16 and 32 bit values from memory.
     */
    protected static native int readByte0(int address);
    protected static native int readShort0(int address);
    protected static native int readInt0(int address);
    protected static native void readBytes0(int address, byte[] dest, int start, int length);
}

